Skip to content

Conversation

@lemoncurdyogurt
Copy link
Member

@lemoncurdyogurt lemoncurdyogurt commented Jan 15, 2026

📢 전달사항

yarn add swiper 추가해주세요!!

  • FullButton 컴포넌트 비활성화상태 디자인 변경돼서 수정해두었습니다.
  • 채팅 작업할 때 HeaderWithLogo컴포넌트 가져와서 알람만 있는 경우 구현해뒀으니 가져다 사용하면 좋을 것 같습니다.
  • 서치필드 채팅목록에서 검색하는 ui가 동일해 common에 넣어두었으니 사용하지면 됩니다.
  • closebutton stroke가 굵은 것 얇은것 두가지 경우가 있어서 두 개 다 다운받았으니 맞춰서 사용해주시면 됩니다.

🔥 작업 내용

홈화면

  • 홈화면에서 스와이퍼 라이브러리 설치 후 슬라이더로 식물 물주기 현황들을 볼 수 있게 하였습니다.
  • 키보드 모듈도 추가해서 방향키로도 넘길 수 있습니다.
  • loop 설정도 해놔서 맨끝에서 우측방향으로 넘길경우 다시 처음으로 넘어옵니다
  • 식물명에서 글래스 스타일 선언해서 가져오는방식으로 하였습니다. 글래스 스타일 하는 경우 global.css에서 유틸리티 가져오면 됩니다.
  • 식물 물주고 나서 24시간 이내의 경우 물주기 완료 상태, 48시간 이내의 경우 물주기 가능 상태, 48시간 이후는 시들기 상태, 식물이 없는 경우엔 Empty상태로 선언하였습니다.
  • 대화상태 컴포넌트에서는 오늘 대화 여부에 따라 대화안한 사람 -> 대화한사람 순으로 order설정해놓았고, 그 내부에서는 대화목표 percentage에 따라서 정렬유틸 추가 해놓았습니다.

친구목록 내용

  • 친구 프로필 조회 모달 추가
  • 친구 편집모드나 프로필 선택에 따라서 헤더나 친구 수 노출 다르게 처리해두었습니다.

🤔 추후 작업 사항

  • 정원작업할 때 식물 다운받아서 추후에 추가할 예정입니다.

  • 친구삭제 단계에서 선택한 프로필보는 곳이랑, 모달 -> 복수선택 가능한지 여부에 따라서 개발할 예정입니다.

  • 친구목록

  • 친구 프로필 조회, 친구 삭제

  • 친구추가 (1/20화)

  • 친구설정 (1/20화)

  • 정원 물주기 시간 카운트다운, 시든시간 카운트업(1/20화)

📸 작업 내역 스크린샷

2026-01-15.5.45.01.mov
스크린샷 2026-01-15 오후 2 25 24 스크린샷 2026-01-15 오후 5 45 50 스크린샷 2026-01-15 오후 5 45 58 image image image image

🔗 이슈

Summary by CodeRabbit

  • 새로운 기능

    • 홈이 식물 진행 카드 갤러리(슬라이더)와 친구 채팅 진행 대시보드로 재구성되었습니다.
    • 친구 목록, 친구 프로필 모달(대화 버튼), 친구 설정·초대 페이지와 친구 추가 흐름이 추가되었습니다.
    • 검색 필드, 헤더(아이콘·펜 버튼 옵션) 및 하단 내비게이션 개선, 식물 상태 버블·진행바·상태별 전면 버튼 테마 도입.
  • 스타일

    • 새로운 색상 변수, 플랜트용 그래디언트·블러·글래스 스타일 및 타이포그래피 확장, 아이콘·간격 조정으로 시각 일관성 향상.
  • 기타

    • 화면용 슬라이더 라이브러리(swiper) 의존성 추가.

✏️ Tip: You can customize this high-level summary in your review settings.

@lemoncurdyogurt lemoncurdyogurt added the feat 기능 추가 label Jan 15, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Walkthrough

홈 화면과 친구 기능을 대거 추가: Swiper 기반 PlantProgress, ChatProgress, 헤더 교체(Header→HeaderWithIcon), 친구 목록·프로필 모달, 관련 타입·유틸·목데이터·스타일 및 swiper 의존성 추가. (≤50단어)

Changes

코호트 / 파일(s) 변경 요약
의존성
\package.json``
swiper ^12.0.3 추가
앱 라우트 / 페이지
\src/app/page.tsx`, `src/app/day-log/page.tsx`, `src/app/shared-diary/[id]/page.tsx`, `src/app/friends/page.tsx`, `src/app/friends/settings/[userId]/page.tsx`, `src/app/friends/invite/page.tsx`, `src/app/friends/add/[code]/page.tsx``
홈 레이아웃 재구성(헤더, PlantProgress, ChatProgress, NavBar), 여러 페이지에서 HeaderHeaderWithIcon 교체, 친구 페이지(목록/설정/초대/추가) 추가 및 간단한 라우트 주석
홈 화면 컴포넌트
\src/components/home/*``
신규 컴포넌트 추가: PlantProgress(Swiper), ChatProgress, Header, Bubble, ProgressBar 및 목데이터/유틸 통합
공통 컴포넌트 / UI
\src/components/common/FullButton.tsx`, `src/components/common/HeaderWithIcon.tsx`, `src/components/common/NavBar.tsx`, `src/components/common/BackHeader.tsx`, `src/components/common/SearchField.tsx`, `src/components/common/GlassStyleHeader.tsx``
FullButtoncolorScheme prop 추가, HeaderWithIcon 도입(타이틀·havePencil), NavBar 간격 조정, BackHeader props 확장(서브텍스트/편집/선택카운트), 검색 필드·GlassStyleHeader 추가
친구 관련 컴포넌트
\src/components/friends/FrinedList.tsx`, `src/components/friends/ProfileModal.tsx`, `src/mock/friendList.json``
친구 목록(검색·선택·모달) 및 프로필 모달 구현, 모의 데이터 추가 (컴포넌트명 오타 FrinedList)
타입·유틸·상수·목데이터
\src/types/`, `src/constants/plantStatus.ts`, `src/utils/`, `src/mock/*``
ChatProgress·FriendProfile·MyProfile 타입 추가/확장, PlantWaterStatus 상수·배경 매핑, getPercentage/getPlantStatus/sortChatProgress 유틸, 목데이터(plantProgress/chatProgress/friendList/myProfile) 추가/수정
아이콘·스타일
\src/assets/GalleryIcon.tsx`, `src/styles/globals.css`, `src/styles/typography.css`, `src/components/dailyPhoto/DailyMissionProgress.tsx`, `src/components/dailyRecord/SharedDiaryChat.tsx``
GalleryIcon 이모지→SVG 교체, 색상·그래디언트·glass 스타일·타이포 토큰 추가, 일부 아이콘(asset) 교체
경미한/기타 변경
\src/components/common/*`, `src/app/day-log/page.tsx`, `src/app/shared-diary/[id]/page.tsx``
헤더 컴포넌트 교체 및 간단한 스타일/구조 조정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HomePage as "HomePage\n(src/app/page.tsx)"
    participant Header as "HeaderWithIcon\n(src/components/common/HeaderWithIcon.tsx)"
    participant PlantProgress as "PlantProgress\n(src/components/home/PlantProgress.tsx)"
    participant ChatProgress as "ChatProgress\n(src/components/home/ChatProgress.tsx)"
    User->>HomePage: 홈 접속
    HomePage->>Header: HeaderWithIcon 렌더
    Header->>User: 친구 버튼 클릭 → 라우트 /friends 이동
    HomePage->>PlantProgress: PlantProgress 렌더
    PlantProgress->>PlantProgress: 목데이터 로드 (`src/mock/plantProgress.json`)
    PlantProgress->>PlantProgress: getPlantStatus → PLANT_BG_BY_STATUS 선택
    PlantProgress->>PlantProgress: Swiper 슬라이드 표시 (Bubble, FullButton)
    HomePage->>ChatProgress: ChatProgress 렌더
    ChatProgress->>ChatProgress: 목데이터 로드 (`src/mock/chatProgress.json`)
    ChatProgress->>ChatProgress: sortChatProgress → getPercentage → ProgressBar 업데이트
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

  • FEAT: 홈화면 #17 — 홈 화면(PlantProgress/ChatProgress/친구 흐름) 요구와 직접적으로 일치함.

Possibly related PRs

🐰 새벽밭을 깡충, 코드씨앗 뿌렸네.
슬라이드와 버블이 반짝이며 춤추고,
친구 창 열면 모달로 얼굴 마주치네.
퍼센트는 길을 비추고 버튼은 손을 내밀어,
정원 한 켠에 새 기능들이 피어났어요. 🌿✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항(홈화면에 식물과 대화정도 표시, 친구 프로필 조회, 친구 삭제)을 명확하게 요약하고 있어 개발자 관점에서 주요 변화를 잘 전달합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/common/HeaderWithIcon.tsx (1)

25-28: router가 선언되지 않아 런타임 에러가 발생합니다.

pencilClick 함수에서 router.push를 호출하지만, useRouter()router를 선언한 적이 없습니다. 또한 이 함수는 Line 48에서 사용되지 않고 인라인 함수가 대신 사용되고 있어 데드 코드입니다.

의도에 따라 두 가지 수정 방안이 있습니다:

🔧 수정 방안 1: pencilClick 함수 사용 시
 export const HeaderWithIcon = ({
   title,
   havePencil = false,
 }: HeaderWithIconProps) => {
+  const router = useRouter();
   const [isAlarmNew, setIsAlarmNew] = useState(true);
   const [isPencilClick, setIsPencilClick] = useState(false);

   const pencilClick = () => {
     setIsPencilClick(true);
     router.push("/shared-diary/upload");
   };
   // ...
         <PencilIcon
           className="h-6 w-6 cursor-pointer"
-          onClick={() => setIsPencilClick(true)}
+          onClick={pencilClick}
         />
🔧 수정 방안 2: 네비게이션 불필요 시 데드 코드 제거
- import { useRouter } from "next/navigation";
-
  import { useState } from "react";
  // ...
  export const HeaderWithIcon = ({
    title,
    havePencil = false,
  }: HeaderWithIconProps) => {
    const [isAlarmNew, setIsAlarmNew] = useState(true);
    const [isPencilClick, setIsPencilClick] = useState(false);

-   const pencilClick = () => {
-     setIsPencilClick(true);
-     router.push("/shared-diary/upload");
-   };
🤖 Fix all issues with AI agents
In `@src/components/home/Bubble.tsx`:
- Line 1: The Bubble.tsx file imports Image from "next/image" but never uses it;
remove the unused import statement (the "Image" import) from the top of the
Bubble component to clean up linter errors and unused-symbol warnings and ensure
no references to Image remain in the Bubble component or its helpers like the
Bubble component function.

In `@src/components/home/PlantProgress.tsx`:
- Line 34: Replace hard-coded status string literals passed to Bubble with the
imported enum/constant PlantWaterStatus; specifically change Bubble
status={"EMPTY"} and the other occurrences on the same file (lines noted in
review: around the usages at lines 56, 68, 86, 88-89) to use
PlantWaterStatus.EMPTY (or the appropriate PlantWaterStatus member) so all
Bubble(...) calls use the PlantWaterStatus constant instead of string literals.
- Around line 23-29: The Swiper is always using loop={true} which can cause DOM
churn or unexpected behaviour when there is one or zero slides; change the loop
prop to be conditional based on the number of plants (e.g., derive a boolean
like shouldLoop = plantLength > 1) and pass that boolean into the Swiper loop
prop inside the PlantProgress component so loop is only enabled when plantLength
> 1.
- Line 77: The JSX in PlantProgress.tsx uses the nonstandard Tailwind class
"z-99" (e.g., in the <p> with className including z-99 and another occurrence at
line 84); change those to the arbitrary value syntax "z-[99]" or alternatively
add a custom z-index token in your CSS theme (src/styles/globals.css `@theme`) and
reference that token, and update both occurrences (the className on the <p> and
the other instance at line 84) accordingly.

In `@src/styles/globals.css`:
- Around line 148-152: The box-shadow declaration contains invalid rgba() calls
without alpha; update the two occurrences of rgba(255, 255, 255) inside the
box-shadow to either rgb(255, 255, 255) or rgba(255, 255, 255, 1) so the CSS is
valid (i.e., change the top two inset color entries in the box-shadow to include
an alpha or use rgb()).
🧹 Nitpick comments (13)
src/utils/getPercentage.ts (1)

2-5: 엣지 케이스 처리 검토 권장.

part === 0 조건은 수학적으로 불필요합니다 (0/total * 100 = 0). 또한 음수 값이나 part > total인 경우의 동작을 고려해 보세요.

♻️ 개선 제안
 // 백분율 값 정수로 계산
 export const getPercentage = (part: number, total: number): number => {
-  if (total === 0 || part === 0) return 0;
-  return Math.round((part / total) * 100);
+  if (total === 0) return 0;
+  const percentage = Math.round((part / total) * 100);
+  return Math.max(0, Math.min(100, percentage)); // 0-100 범위로 제한
 };
src/components/home/Header.tsx (1)

7-33: 전반적으로 LGTM!

홈 헤더 컴포넌트가 잘 구현되어 있습니다. 몇 가지 개선 포인트가 있습니다:

  1. Line 27의 AlarmIconcursor-pointer가 적용되어 있지만 클릭 핸들러가 없어 사용자 경험에 혼란을 줄 수 있습니다.
  2. Line 14-16의 FriendIcon도 클릭 가능해 보이지만 핸들러가 없습니다 (향후 친구 기능 추가 예정이라면 괜찮습니다).
♻️ 접근성 개선 제안
             <button
               className="relative cursor-pointer"
               onClick={() => setIsAlarmNew(false)}
+              aria-label="새 알림 확인"
             >
src/components/common/FullButton.tsx (2)

20-27: 중첩 삼항 연산자 대신 객체 매핑 사용을 권장합니다.

현재 중첩된 삼항 연산자는 가독성이 떨어지고, text-button-sb가 모든 분기에서 중복됩니다. 또한 Line 40의 <span>에도 동일한 클래스가 적용되어 이중 적용됩니다.

♻️ 객체 매핑으로 리팩터링
+ const colorSchemeClasses = {
+   mint: "bg-mint-01",
+   orange: "bg-orange-00",
+   blue: "bg-blue-00",
+ } as const;
+
- const activeClass =
-   colorScheme === "mint"
-     ? "bg-mint-01 text-white text-button-sb"
-     : colorScheme === "orange"
-       ? "bg-orange-00 text-white text-button-sb"
-       : colorScheme === "blue"
-         ? "bg-blue-00 text-white text-button-sb"
-         : "";
+ const activeClass = `${colorSchemeClasses[colorScheme]} text-white text-button-sb`;

39-41: text-button-sb 클래스 중복 적용

activeClassinactiveClass에 이미 text-button-sb가 포함되어 있는데, Line 40의 <span>에도 동일한 클래스가 적용되어 있습니다. 중복을 제거하세요.

♻️ 중복 클래스 제거
       <div className="flex h-[58px] items-center justify-center">
-        <span className="text-button-sb">{children}</span>
+        <span>{children}</span>
       </div>
src/components/home/ProgressBar.tsx (1)

1-17: LGTM - 단순하고 효과적인 구현입니다.

한 가지 방어적 코딩 제안: percentage 값이 0-100 범위를 벗어날 경우를 대비하여 클램핑을 고려해 보세요.

♻️ 방어적 코딩 제안
 export const ProgressBar = ({
   percentage,
   haveTodayChat,
 }: ProgressBarProps) => {
+  const clampedPercentage = Math.min(100, Math.max(0, percentage));
   return (
     <div className="bg-neutral-08 flex h-2 w-full rounded-sm">
       <div
         className={`${haveTodayChat ? "bg-mint-02" : "bg-orange-01"} rounded-s-sm`}
-        style={{ width: `${percentage}%` }}
+        style={{ width: `${clampedPercentage}%` }}
       ></div>
     </div>
   );
 };
src/components/common/HeaderWithIcon.tsx (1)

8-8: 사용되지 않는 import

FriendIcon이 import되었지만 컴포넌트에서 사용되지 않습니다. 제거하거나, 향후 사용 예정이라면 TODO 주석을 추가하세요.

♻️ 미사용 import 제거
 import AlarmIcon from "@/assets/bell.svg";
-import FriendIcon from "@/assets/friend.svg";
 import PencilIcon from "@/assets/pencil.svg";
src/app/page.tsx (1)

19-21: NavBar 래퍼의 중복된 fixed 포지셔닝 확인 필요.

NavBar 컴포넌트 내부에 이미 fixed bottom-0 left-1/2 -translate-x-1/2가 적용되어 있습니다. 외부 래퍼 div에 추가로 fixed bottom-0를 적용하면 예상치 못한 레이아웃 문제가 발생할 수 있습니다.

래퍼를 제거하거나, NavBar 컴포넌트의 포지셔닝 방식을 통일하는 것을 권장합니다.

♻️ 제안된 수정
       <div className="pb-[119px]">
         <ChatProgress />
       </div>
-      <div className="fixed bottom-0 w-full max-w-[440px]">
-        <NavBar />
-      </div>
+      <NavBar />
     </main>
src/utils/getPlantStatus.ts (1)

3-5: 타입 정의와 유효하지 않은 날짜 처리 개선 필요

  1. 파라미터 타입이 string이지만, falsy 체크(!recentWateredTime)는 null이나 undefined를 처리하려는 의도로 보입니다. 타입을 명확히 하는 것이 좋습니다.
  2. 유효하지 않은 날짜 문자열이 전달되면 NaN이 반환되어 예기치 않은 동작이 발생할 수 있습니다.
♻️ 개선 제안
-export const getPlantStatus = (recentWateredTime: string) => {
-  if (!recentWateredTime) return PlantWaterStatus.EMPTY;
-  const wateredAt = new Date(recentWateredTime).getTime();
+export const getPlantStatus = (recentWateredTime: string | null | undefined) => {
+  if (!recentWateredTime) return PlantWaterStatus.EMPTY;
+  const wateredAt = new Date(recentWateredTime).getTime();
+  if (Number.isNaN(wateredAt)) return PlantWaterStatus.EMPTY;
src/components/home/ChatProgress.tsx (3)

10-11: 정렬 로직 메모이제이션 고려

sortChatProgress가 매 렌더링마다 호출됩니다. 데이터가 변경되지 않는 한 불필요한 재정렬을 방지하기 위해 useMemo를 사용하는 것이 좋습니다.

♻️ useMemo 적용 제안
+import { useMemo } from "react";
+
 export const ChatProgress = () => {
-  const sortedData = sortChatProgress(chatProgressData);
+  const sortedData = useMemo(
+    () => sortChatProgress(chatProgressData),
+    [chatProgressData]
+  );

19-61: 빈 데이터 상태 처리 누락

chatProgressData가 비어있을 경우 아무것도 렌더링되지 않습니다. PlantProgress 컴포넌트처럼 빈 상태에 대한 UI를 추가하는 것이 좋습니다.

♻️ 빈 상태 처리 예시
       <div className="px-4 py-[10px]">
+        {sortedData.length === 0 ? (
+          <p className="text-neutral-05 text-center">이번 주 대화 기록이 없어요</p>
+        ) : (
         {sortedData.map(data => {
           // ... existing code
         })}
+        )}
       </div>

24-30: 이미지 로딩 실패 시 fallback 처리 고려

profileImg가 유효하지 않거나 로딩에 실패할 경우 깨진 이미지가 표시될 수 있습니다. 기본 프로필 이미지나 placeholder를 고려해 보세요.

src/components/home/Bubble.tsx (2)

16-30: switch문에 default case 또는 명시적 반환 타입 추가 권장

현재 모든 PlantWaterStatus 케이스를 처리하지만, 명시적 반환 타입이나 default case를 추가하면 타입 안전성이 향상됩니다. 예상치 못한 값이 전달될 경우 undefined가 반환될 수 있습니다.

♻️ 개선 제안
-export const Bubble = ({ status }: BubbleProps) => {
+export const Bubble = ({ status }: BubbleProps): JSX.Element => {
   switch (status) {
     case PlantWaterStatus.EMPTY: {
       return <YellowCircle />;
     }
     case PlantWaterStatus.WATERED_RECENTLY: {
       return <GreenCircle />;
     }
     case PlantWaterStatus.WATERABLE: {
       return <BlueCircle />;
     }
     case PlantWaterStatus.WITHERED: {
       return <RedCircle />;
     }
+    default: {
+      const _exhaustiveCheck: never = status;
+      return <YellowCircle />;
+    }
   }
 };

3-6: 파일 명명 규칙 불일치

red_circle.svg는 언더스코어를 사용하지만, 다른 파일들은 하이픈을 사용합니다(blue-circle.svg, green-circle.svg, yellow-circle.svg). 일관성을 위해 red-circle.svg로 변경하는 것을 권장합니다.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 314dd5a and e5e41be.

⛔ Files ignored due to path filters (8)
  • src/assets/bell.svg is excluded by !**/*.svg
  • src/assets/clock.svg is excluded by !**/*.svg
  • src/assets/friend.svg is excluded by !**/*.svg
  • src/assets/home/blue-circle.svg is excluded by !**/*.svg
  • src/assets/home/green-circle.svg is excluded by !**/*.svg
  • src/assets/home/red_circle.svg is excluded by !**/*.svg
  • src/assets/home/yellow-circle.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (20)
  • package.json
  • src/app/day-log/page.tsx
  • src/app/page.tsx
  • src/app/shared-diary/[id]/page.tsx
  • src/components/common/FullButton.tsx
  • src/components/common/HeaderWithIcon.tsx
  • src/components/common/NavBar.tsx
  • src/components/home/Bubble.tsx
  • src/components/home/ChatProgress.tsx
  • src/components/home/Header.tsx
  • src/components/home/PlantProgress.tsx
  • src/components/home/ProgressBar.tsx
  • src/constants/plantStatus.ts
  • src/mock/chatProgress.json
  • src/mock/plantProgress.json
  • src/styles/globals.css
  • src/types/chatProgress.type.ts
  • src/utils/getPercentage.ts
  • src/utils/getPlantStatus.ts
  • src/utils/sortChatProgress.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-02T13:08:01.441Z
Learnt from: lemoncurdyogurt
Repo: IT-Cotato/12th-SimTok-FE PR: 3
File: src/utils/formatPhone.ts:6-9
Timestamp: 2026-01-02T13:08:01.441Z
Learning: In phone number formatting/validation logic, enforce that Korean numbers are valid only as 10-digit landlines or 11-digit mobile numbers starting with 010. Do not perform strict intermediate-length formatting for 7–9 digits during input; assume intermediate values are not considered valid until they reach 10 or 11 digits. Implement or update validation to accept only 10 or 11 digits (with 010 prefix for mobiles) and, if formatting is needed during input, preserve user-friendly partial handling without emitting strict validation errors for intermediate states.

Applied to files:

  • src/utils/getPercentage.ts
  • src/utils/sortChatProgress.ts
  • src/utils/getPlantStatus.ts
🧬 Code graph analysis (9)
src/types/chatProgress.type.ts (1)
src/components/home/ChatProgress.tsx (1)
  • ChatProgress (10-65)
src/utils/sortChatProgress.ts (2)
src/components/home/ChatProgress.tsx (1)
  • ChatProgress (10-65)
src/types/chatProgress.type.ts (1)
  • ChatProgress (1-8)
src/components/home/PlantProgress.tsx (5)
src/components/home/Bubble.tsx (1)
  • Bubble (16-31)
src/components/dailyRecord/InfoMessage.tsx (1)
  • InfoMessage (5-19)
src/components/common/FullButton.tsx (1)
  • FullButton (11-44)
src/utils/getPlantStatus.ts (1)
  • getPlantStatus (3-13)
src/constants/plantStatus.ts (2)
  • PLANT_BG_BY_STATUS (11-16)
  • PlantWaterStatus (1-6)
src/app/day-log/page.tsx (1)
src/components/common/HeaderWithIcon.tsx (1)
  • HeaderWithIcon (18-59)
src/app/shared-diary/[id]/page.tsx (1)
src/components/common/HeaderWithIcon.tsx (1)
  • HeaderWithIcon (18-59)
src/components/home/ChatProgress.tsx (4)
src/types/chatProgress.type.ts (1)
  • ChatProgress (1-8)
src/utils/sortChatProgress.ts (1)
  • sortChatProgress (3-14)
src/utils/getPercentage.ts (1)
  • getPercentage (2-5)
src/components/home/ProgressBar.tsx (1)
  • ProgressBar (5-17)
src/utils/getPlantStatus.ts (1)
src/constants/plantStatus.ts (1)
  • PlantWaterStatus (1-6)
src/components/home/Bubble.tsx (1)
src/constants/plantStatus.ts (2)
  • PlantWaterStatusType (8-9)
  • PlantWaterStatus (1-6)
src/app/page.tsx (4)
src/components/home/Header.tsx (1)
  • Header (7-33)
src/components/home/PlantProgress.tsx (1)
  • PlantProgress (18-105)
src/components/home/ChatProgress.tsx (1)
  • ChatProgress (10-65)
src/components/common/NavBar.tsx (1)
  • NavBar (8-31)
🔇 Additional comments (13)
src/mock/plantProgress.json (1)

1-32: LGTM!

Mock 데이터 구조가 일관성 있게 잘 정의되어 있습니다. ISO 8601 날짜 형식을 사용하고 있어 파싱 및 처리에 용이합니다.

src/styles/globals.css (1)

86-124: LGTM!

식물 상태별 그라디언트 유틸리티 클래스가 잘 정의되어 있습니다. 각 상태(blue, yellow, red, green)에 맞는 시각적 구분이 명확합니다.

src/components/common/NavBar.tsx (1)

21-25: LGTM!

아이콘과 라벨 사이에 gap-[5px]를 추가하여 간격을 개선했습니다. TODO 주석으로 box-shadow 디자인 작업이 남아있음을 명확히 표시한 점도 좋습니다.

package.json (1)

27-27: Swiper 12.0.3 버전은 유효하며 보안 이슈가 없습니다.

버전 12.0.3은 npm 레지스트리에 존재하는 유효한 버전입니다. Swiper의 알려진 보안 취약점(CVE-2021-23370)은 6.5.1 이전 버전에만 영향을 미쳤으며 이미 패치되었으므로, 12.0.3에는 직접적인 보안 위협이 없습니다. PlantProgress 컴포넌트에서도 올바르게 사용 중입니다.

src/app/shared-diary/[id]/page.tsx (1)

23-27: LGTM!

HeaderWithIcon으로의 마이그레이션이 잘 적용되었습니다. justify-center 추가로 레이아웃이 개선되었습니다.

src/app/day-log/page.tsx (1)

1-1: LGTM!

HeaderWithIcon 컴포넌트로의 변경이 올바르게 적용되었습니다. titlehavePencil props가 적절히 전달되고 있습니다.

Also applies to: 15-15

src/mock/chatProgress.json (1)

1-82: LGTM!

목업 데이터가 ChatProgress 타입 정의와 일치하며, 개발 및 테스트 목적으로 적절하게 구성되어 있습니다.

src/app/page.tsx (1)

7-18: LGTM!

페이지 레이아웃 구조가 잘 구성되어 있습니다. Header, PlantProgress, ChatProgress 섹션이 적절하게 배치되었습니다.

src/utils/sortChatProgress.ts (1)

3-14: LGTM! 정렬 순서 의도 확인.

정렬 로직이 올바르게 구현되어 있습니다:

  • hasTodayChatfalse인 항목이 먼저 정렬됨 (PR 요구사항 "대화 안 한 사람 → 대화한 사람"과 일치)
  • 0으로 나누기 방지 처리 완료
  • 원본 배열 변경 없이 복사본 정렬

현재 rate는 오름차순으로 정렬됩니다 (낮은 percentage가 먼저). 이것이 의도된 동작인지 확인해 주세요.

src/types/chatProgress.type.ts (1)

1-8: LGTM!

타입 정의가 명확하고 목업 데이터 구조와 일치합니다. 관련 컴포넌트와 유틸리티에서 올바르게 사용되고 있습니다.

src/utils/getPlantStatus.ts (1)

7-12: LGTM!

시간 차이 계산과 상태 임계값 로직이 PR 요구사항(24시간 이내: WATERED_RECENTLY, 24-48시간: WATERABLE, 48시간 이후: WITHERED)에 맞게 올바르게 구현되어 있습니다.

src/constants/plantStatus.ts (1)

1-16: LGTM!

상수 정의가 깔끔하고 as const를 활용한 타입 안전성이 잘 구현되어 있습니다. PLANT_BG_BY_STATUS 매핑이 모든 상태를 커버하고 있습니다.

src/components/home/PlantProgress.tsx (1)

76-82: 조건부 로직 의도 확인 필요

식물이 1개일 때 "원하는 식물을 골라보세요!" 메시지가 표시됩니다. 이미 식물을 키우고 있는 사용자에게 이 메시지가 적절한지 확인이 필요합니다. 식물명을 표시하거나 다른 메시지를 보여주는 것이 더 적절할 수 있습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@@ -0,0 +1,31 @@
import Image from "next/image";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

사용되지 않는 import 제거

Imagenext/image에서 import되었지만 컴포넌트에서 사용되지 않습니다.

♻️ 수정 제안
-import Image from "next/image";
-
 import BlueCircle from "@/assets/home/blue-circle.svg";
🤖 Prompt for AI Agents
In `@src/components/home/Bubble.tsx` at line 1, The Bubble.tsx file imports Image
from "next/image" but never uses it; remove the unused import statement (the
"Image" import) from the top of the Bubble component to clean up linter errors
and unused-symbol warnings and ensure no references to Image remain in the
Bubble component or its helpers like the Bubble component function.

Comment on lines +23 to +29
<Swiper
modules={[Keyboard]}
keyboard={{ enabled: true }}
loop={true}
spaceBetween={16}
slidesPerView={1}
>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /tmp && find . -type f -name "PlantProgress.tsx" 2>/dev/null | head -5

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

find . -type f -name "*.tsx" -o -name "*.ts" | grep -i plant | head -10

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 168


🏁 Script executed:

find . -type f -path "*components/home/PlantProgress.tsx"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 107


🏁 Script executed:

cat -n ./src/components/home/PlantProgress.tsx | head -60

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 2535


🏁 Script executed:

wc -l ./src/components/home/PlantProgress.tsx

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 111


🏁 Script executed:

cat -n ./src/components/home/PlantProgress.tsx | sed -n '60,105p'

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 2267


단일 또는 소수의 슬라이드에서 loop 동작 비활성화 권장

loop={true}가 항상 활성화되어 있지만, 슬라이드가 1개 이하일 때(plantLength ≤ 1) Swiper의 loop 모드는 불필요한 DOM 조작을 발생시키고 예상치 못한 동작을 유발할 수 있습니다. 여러 개의 슬라이드가 있을 때만 loop를 활성화하는 것이 Swiper 모범 사례입니다.

♻️ 조건부 loop 적용
       <Swiper
         modules={[Keyboard]}
         keyboard={{ enabled: true }}
-        loop={true}
+        loop={plantLength > 1}
         spaceBetween={16}
         slidesPerView={1}
       >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Swiper
modules={[Keyboard]}
keyboard={{ enabled: true }}
loop={true}
spaceBetween={16}
slidesPerView={1}
>
<Swiper
modules={[Keyboard]}
keyboard={{ enabled: true }}
loop={plantLength > 1}
spaceBetween={16}
slidesPerView={1}
>
🤖 Prompt for AI Agents
In `@src/components/home/PlantProgress.tsx` around lines 23 - 29, The Swiper is
always using loop={true} which can cause DOM churn or unexpected behaviour when
there is one or zero slides; change the loop prop to be conditional based on the
number of plants (e.g., derive a boolean like shouldLoop = plantLength > 1) and
pass that boolean into the Swiper loop prop inside the PlantProgress component
so loop is only enabled when plantLength > 1.

<SwiperSlide>
<div className="bg-plant-yellow relative h-[551px] w-full overflow-hidden">
<div className="flex h-full flex-col items-center justify-center px-4">
<Bubble status={"EMPTY"} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

문자열 리터럴 대신 상수 사용 권장

"EMPTY" 문자열 대신 import된 PlantWaterStatus.EMPTY 상수를 사용하세요. 이 패턴이 lines 56, 68, 86, 88-89에도 적용됩니다.

♻️ 상수 사용으로 일관성 개선
-                <Bubble status={"EMPTY"} />
+                <Bubble status={PlantWaterStatus.EMPTY} />

Lines 68, 86, 88-89도 동일하게 수정:

-                        ? "오늘은 물주기를 완료했어요🥳"
+                      {plantStatus === PlantWaterStatus.WATERED_RECENTLY
+                        ? "오늘은 물주기를 완료했어요🥳"

-                        isActive={!(plantStatus === "WATERED_RECENTLY")}
+                        isActive={plantStatus !== PlantWaterStatus.WATERED_RECENTLY}

-                          (plantStatus === "WATERABLE" && "blue") ||
-                          (plantStatus === "WITHERED" && "orange") ||
+                          (plantStatus === PlantWaterStatus.WATERABLE && "blue") ||
+                          (plantStatus === PlantWaterStatus.WITHERED && "orange") ||
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Bubble status={"EMPTY"} />
<Bubble status={PlantWaterStatus.EMPTY} />
🤖 Prompt for AI Agents
In `@src/components/home/PlantProgress.tsx` at line 34, Replace hard-coded status
string literals passed to Bubble with the imported enum/constant
PlantWaterStatus; specifically change Bubble status={"EMPTY"} and the other
occurrences on the same file (lines noted in review: around the usages at lines
56, 68, 86, 88-89) to use PlantWaterStatus.EMPTY (or the appropriate
PlantWaterStatus member) so all Bubble(...) calls use the PlantWaterStatus
constant instead of string literals.

<div className="bg-blur absolute bottom-0 z-10 h-[329px] w-full max-w-[440px]" />
<div className="absolute bottom-[84px] z-30 flex w-full items-center justify-center px-4 py-[10px]">
{plantLength > 1 ? (
<p className="text-h3 text-neutral-05 bg-glass-style z-99 rounded-2xl p-[10px]">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find Tailwind configuration files
find . -name "tailwind.config.*" -o -name "tailwind.config.js" -o -name "tailwind.config.ts" | head -20

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Search for z-99 usage and zIndex definitions
rg "z-99|zIndex" --type=js --type=ts --type=json -B 2 -A 2

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1847


🏁 Script executed:

# Check PlantProgress.tsx context around lines 77 and 84
fd "PlantProgress.tsx" --exec head -90 {} \; | tail -25

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1388


🏁 Script executed:

# Search for Tailwind configuration in all common locations
find . -type f \( -name "*.config.*" -o -name "postcss.config.*" \) 2>/dev/null | grep -E "(tailwind|postcss|next)"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 105


🏁 Script executed:

# Check for Tailwind setup in package.json or tsconfig
rg "tailwind|zIndex" --type=json -A 3 -B 1 | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 511


🏁 Script executed:

# Look for any extend or theme configuration related to z-index
rg "extend.*theme|theme.*extend" --type=js --type=ts -A 10 | head -80

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Check postcss.config.mjs
cat postcss.config.mjs

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 161


🏁 Script executed:

# Look for CSS files that might define theme extensions
fd "\\.css$" | head -20

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 116


🏁 Script executed:

# Check for any CSS files with zIndex or theme definitions
rg "zIndex|--z-|extend" --type=css -B 2 -A 2

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Check globals.css
cat src/styles/globals.css

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 3969


🏁 Script executed:

# Check typography.css
cat src/styles/typography.css

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 2433


🏁 Script executed:

# Search for any theme or config definitions in all CSS files
rg "@theme|@layer|z-" --type=css

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 176


Tailwind v4에서 정의되지 않은 z-index 클래스

z-99는 Tailwind v4의 표준 클래스가 아닙니다. 임의 값을 사용하려면 z-[99] 형식을 사용하거나, src/styles/globals.css@theme 블록에 --z-99 정의를 추가하세요. Line 84에도 동일한 이슈가 있습니다.

🤖 Prompt for AI Agents
In `@src/components/home/PlantProgress.tsx` at line 77, The JSX in
PlantProgress.tsx uses the nonstandard Tailwind class "z-99" (e.g., in the <p>
with className including z-99 and another occurrence at line 84); change those
to the arbitrary value syntax "z-[99]" or alternatively add a custom z-index
token in your CSS theme (src/styles/globals.css `@theme`) and reference that
token, and update both occurrences (the className on the <p> and the other
instance at line 84) accordingly.

Comment on lines +148 to +152
box-shadow:
inset 1px 1px 0 rgba(255, 255, 255),
inset -1px -1px 0 rgba(255, 255, 255),
inset 0 0 34px rgba(255, 255, 255, 0.55),
inset 0 6px 12px rgba(255, 255, 255, 0.12);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

rgba() 구문 오류 수정 필요.

rgba(255, 255, 255)는 알파 값이 누락되어 유효하지 않은 CSS입니다. rgba()는 4개의 인자가 필요하거나, rgb()를 사용해야 합니다.

🐛 수정 제안
     box-shadow:
-      inset 1px 1px 0 rgba(255, 255, 255),
-      inset -1px -1px 0 rgba(255, 255, 255),
+      inset 1px 1px 0 rgba(255, 255, 255, 1),
+      inset -1px -1px 0 rgba(255, 255, 255, 1),
       inset 0 0 34px rgba(255, 255, 255, 0.55),
       inset 0 6px 12px rgba(255, 255, 255, 0.12);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
box-shadow:
inset 1px 1px 0 rgba(255, 255, 255),
inset -1px -1px 0 rgba(255, 255, 255),
inset 0 0 34px rgba(255, 255, 255, 0.55),
inset 0 6px 12px rgba(255, 255, 255, 0.12);
box-shadow:
inset 1px 1px 0 rgba(255, 255, 255, 1),
inset -1px -1px 0 rgba(255, 255, 255, 1),
inset 0 0 34px rgba(255, 255, 255, 0.55),
inset 0 6px 12px rgba(255, 255, 255, 0.12);
🤖 Prompt for AI Agents
In `@src/styles/globals.css` around lines 148 - 152, The box-shadow declaration
contains invalid rgba() calls without alpha; update the two occurrences of
rgba(255, 255, 255) inside the box-shadow to either rgb(255, 255, 255) or
rgba(255, 255, 255, 1) so the CSS is valid (i.e., change the top two inset color
entries in the box-shadow to include an alpha or use rgb()).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/components/home/ChatProgress.tsx`:
- Around line 20-22: The Tailwind class on the EyesIcon is incomplete ("w-");
update the className on the EyesIcon component in ChatProgress.tsx to include a
valid width utility (for example replace "w- h-[29px]" with "w-6 h-[29px]" or
"w-[24px] h-[29px]") so the icon receives the intended width.
- Around line 30-36: The Tailwind size classes on the Image in ChatProgress.tsx
are invalid; update the Image component (props: src={data.profileImg},
alt={data.userName}, width={72}, height={72}) to either remove the h-18 w-18
classes from className or replace them with valid utilities like h-[72px]
w-[72px] (keeping rounded-2xl object-cover), and since data.profileImg uses
local paths, remove any unnecessary remotePatterns configuration from
next.config.js to avoid needless remote image rules.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5e41be and d007bd9.

⛔ Files ignored due to path filters (2)
  • src/assets/hand-with-pen.svg is excluded by !**/*.svg
  • src/assets/two-eyes.svg is excluded by !**/*.svg
📒 Files selected for processing (2)
  • src/assets/GalleryIcon.tsx
  • src/components/home/ChatProgress.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/home/ChatProgress.tsx (4)
src/types/chatProgress.type.ts (1)
  • ChatProgress (1-8)
src/utils/sortChatProgress.ts (1)
  • sortChatProgress (3-14)
src/utils/getPercentage.ts (1)
  • getPercentage (2-5)
src/components/home/ProgressBar.tsx (1)
  • ProgressBar (5-17)
🔇 Additional comments (5)
src/assets/GalleryIcon.tsx (3)

2-3: LGTM!

SVG 아이콘 임포트가 정상적으로 추가되었습니다. 이모지를 SVG로 교체하는 것은 접근성과 일관성 측면에서 좋은 개선입니다.


9-10: 아이콘 크기가 컨테이너를 초과합니다.

컨테이너 크기는 h-[15px] w-[15px]인데, EyesIcon의 너비가 w-[18px]로 설정되어 있어 오버플로우가 발생합니다. 의도된 디자인이라면 무시해도 되지만, 그렇지 않다면 아이콘 크기를 컨테이너에 맞게 조정하거나 overflow-hidden을 추가하는 것을 고려해 주세요.


12-13: LGTM!

HandIcon 크기(13px)가 컨테이너(15px) 내에 적절하게 배치되어 있습니다.

src/components/home/ChatProgress.tsx (2)

1-11: LGTM!

import 구조가 깔끔하게 정리되어 있습니다. SVG를 React 컴포넌트로, mock 데이터와 유틸리티 함수들을 적절히 분리하여 사용하고 있습니다.


26-67: LGTM!

사용자 목록 렌더링 로직이 잘 구현되어 있습니다:

  • userId를 key로 사용하여 고유성 보장
  • getPercentage 유틸리티를 활용한 진행률 계산
  • hasTodayChat 상태에 따른 조건부 메시지 표시
  • ProgressBar 컴포넌트와의 연동이 적절함

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +20 to +22
<span>
<EyesIcon className="w- h-[29px]" />
</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

불완전한 Tailwind 클래스명 수정 필요.

w- 클래스가 불완전합니다. 값이 누락되어 너비가 적용되지 않습니다. 의도한 너비 값을 추가해주세요 (예: w-6, w-[24px] 등).

🔧 수정 제안
         <span>
-          <EyesIcon className="w- h-[29px]" />
+          <EyesIcon className="w-[29px] h-[29px]" />
         </span>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span>
<EyesIcon className="w- h-[29px]" />
</span>
<span>
<EyesIcon className="w-[29px] h-[29px]" />
</span>
🤖 Prompt for AI Agents
In `@src/components/home/ChatProgress.tsx` around lines 20 - 22, The Tailwind
class on the EyesIcon is incomplete ("w-"); update the className on the EyesIcon
component in ChatProgress.tsx to include a valid width utility (for example
replace "w- h-[29px]" with "w-6 h-[29px]" or "w-[24px] h-[29px]") so the icon
receives the intended width.

Comment on lines +30 to +36
<Image
src={data.profileImg}
alt={data.userName}
width={72}
height={72}
className="h-18 w-18 rounded-2xl object-cover"
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for next.config file and examine its content
fd "next\.config\.(js|ts|mjs)" --type f -x cat {}

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 634


🏁 Script executed:

#!/bin/bash
# Check for tailwind.config file
fd "tailwind\.config\.(js|ts)" --type f -x cat {}

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Check if mock data file exists and examine profileImg URLs
if [ -f "src/mock/chatProgress.json" ]; then
  cat src/mock/chatProgress.json
elif [ -f "src/mocks/chatProgress.json" ]; then
  cat src/mocks/chatProgress.json
else
  echo "Mock file not found, searching for similar files..."
  fd "chatProgress\|chat.*progress" --type f
fi

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1557


🏁 Script executed:

#!/bin/bash
# Examine the ChatProgress.tsx component
fd "ChatProgress\.tsx" --type f -x cat {}

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 2520


🏁 Script executed:

#!/bin/bash
# Search for any h-18 or w-18 references in tailwind config or CSS
rg "h-18|w-18" --type js --type ts --type css

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 167


🏁 Script executed:

#!/bin/bash
# Search for tailwind config in various locations and formats
find . -name "tailwind.config.*" -o -name "tailwind.css" -o -name "globals.css" 2>/dev/null | head -20

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Check for any configuration files in root
ls -la | grep -E "tailwind|postcss"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 138


🏁 Script executed:

#!/bin/bash
# Search for h-18 and w-18 definitions in CSS and config files
rg "h-18|w-18" --type css --type scss

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Check if there's a custom theme extension in any config
rg "theme.*extend|spacing" --type js --type ts --max-count=50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Check package.json to understand the tailwind setup
cat package.json | grep -A 10 -B 2 "tailwind"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 562


🏁 Script executed:

#!/bin/bash
# Check globals.css for custom h-18 and w-18 definitions
cat src/styles/globals.css

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 3969


🏁 Script executed:

#!/bin/bash
# Check postcss config
cat postcss.config.mjs

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Check if there's any tailwind config in tsconfig or other places
find . -name "tsconfig.json" -o -name ".tailwindrc*" 2>/dev/null

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 83


🏁 Script executed:

#!/bin/bash
# Search for any h- or w- class definitions in the entire codebase
rg "@layer|@apply|h-\[|w-\[|h-[0-9]|w-[0-9]" --type css --type js --type ts -A 2 | head -100

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 7858


Tailwind 클래스 h-18, w-18은 표준 클래스가 아니므로 수정 필요.

Image 컴포넌트에서 className="h-18 w-18 rounded-2xl object-cover"로 지정한 클래스는 Tailwind의 기본 spacing scale에 존재하지 않습니다. width={72}, height={72} props로 크기가 이미 제어되고 있으므로, h-[72px] w-[72px]으로 변경하거나 className에서 크기 관련 클래스를 제거하세요.

참고: data.profileImg는 mock 데이터에서 모두 로컬 경로(/images/1.jpg 등)이므로 next.config.jsremotePatterns 설정은 필요하지 않습니다.

🤖 Prompt for AI Agents
In `@src/components/home/ChatProgress.tsx` around lines 30 - 36, The Tailwind size
classes on the Image in ChatProgress.tsx are invalid; update the Image component
(props: src={data.profileImg}, alt={data.userName}, width={72}, height={72}) to
either remove the h-18 w-18 classes from className or replace them with valid
utilities like h-[72px] w-[72px] (keeping rounded-2xl object-cover), and since
data.profileImg uses local paths, remove any unnecessary remotePatterns
configuration from next.config.js to avoid needless remote image rules.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/components/common/HeaderWithIcon.tsx`:
- Line 40: The span element in HeaderWithIcon.tsx uses an invalid Tailwind class
"bg-red-00" so the notification dot doesn't render; update the className on the
span (the notification dot in the HeaderWithIcon component) to use a valid
Tailwind background color such as "bg-red-500" (or another valid color class)
while keeping the positioning and sizing classes unchanged.

In `@src/components/friends/FrinedList.tsx`:
- Around line 1-4: The file name contains a typo: rename FrinedList.tsx to
FriendList.tsx and update any references/imports to use FriendList (e.g., import
FriendList from ".../FriendList") and ensure the component declaration/export
inside (component name, default export) is also corrected to FriendList so the
component name and filename match.

In `@src/components/home/Header.tsx`:
- Around line 29-31: The AlarmIcon rendering branch for isAlarmNew = false shows
a cursor-pointer but isn’t interactive; either remove the "cursor-pointer" class
from the non-interactive AlarmIcon or make it interactive to match the true
branch by wrapping the AlarmIcon in the same button element/handler used when
isAlarmNew is true (use the same onClick/navigation logic). Update the JSX
around isAlarmNew and the AlarmIcon to ensure visual affordance matches actual
behavior.
🧹 Nitpick comments (8)
src/components/common/HeaderWithIcon.tsx (2)

45-50: 접근성 개선: PencilIcon을 버튼으로 감싸세요.

AlarmIcon과 달리 PencilIcon이 버튼 없이 직접 클릭 핸들러를 가지고 있습니다. 스크린 리더와 키보드 내비게이션을 위해 버튼으로 감싸는 것이 좋습니다.

♻️ 접근성 개선 제안
         {havePencil && (
-          <PencilIcon
-            className="h-6 w-6 cursor-pointer"
-            onClick={pencilClick}
-          />
+          <button className="cursor-pointer" onClick={pencilClick}>
+            <PencilIcon className="h-6 w-6" />
+          </button>
         )}

42-44: cursor-pointer가 있지만 클릭 핸들러가 없습니다.

isAlarmNew가 false일 때 AlarmIcon에 cursor-pointer가 있지만 클릭 이벤트가 없어서 사용자에게 혼란을 줄 수 있습니다.

♻️ 수정 제안
         ) : (
-          <AlarmIcon className="h-6 w-6 cursor-pointer" />
+          <AlarmIcon className="h-6 w-6" />
         )}
src/components/home/Header.tsx (1)

17-19: 아이콘 버튼에 접근성 속성 추가를 권장합니다.

아이콘만 있는 버튼의 경우 스크린 리더 사용자를 위해 aria-label을 추가하는 것이 좋습니다. 또한 버튼 내부의 cursor-pointer는 중복이므로 제거할 수 있습니다.

♿ 접근성 개선 제안
-        <button onClick={() => router.push("/friends")}>
-          <FriendIcon className="text-neutral-03 cursor-pointer" />
+        <button onClick={() => router.push("/friends")} aria-label="친구 목록">
+          <FriendIcon className="text-neutral-03" />
         </button>

AlarmIcon 버튼에도 동일하게 적용:

             <button
               className="relative flex cursor-pointer items-center justify-center"
               onClick={() => setIsAlarmNew(false)}
+              aria-label="새 알림"
             >
-              <AlarmIcon className="text-neutral-03 h-6 w-6 cursor-pointer" />
+              <AlarmIcon className="text-neutral-03 h-6 w-6" />
src/components/friends/SearchField.tsx (1)

9-13: 접근성: input에 label이 필요합니다.

스크린 리더 사용자를 위해 <label>을 추가하거나 aria-label 속성을 사용해주세요. 또한 input의 배경색이 부모와 다를 수 있으므로 bg-transparent를 추가하는 것이 좋습니다.

♻️ 개선 제안
         <input
           type="text"
           placeholder="친구이름을 검색해보세요"
-          className="w-full focus:outline-none"
-        ></input>
+          aria-label="친구 검색"
+          className="w-full bg-transparent focus:outline-none"
+        />
src/components/common/BackHeader.tsx (1)

49-52: subtext 버튼에 type과 onClick 핸들러가 필요합니다.

"편집하기" 같은 텍스트가 버튼으로 렌더링되지만, 클릭 시 동작이 정의되어 있지 않습니다. onClick prop을 추가하거나, 동작이 없다면 <span>으로 변경하는 것을 고려해주세요.

♻️ onClick prop 추가 제안
 interface HeaderProps {
   title: string;
   timeAgo?: string;
   menuIcon?: boolean;
   titleColor?: string;
   subtext?: string;
+  onSubtextClick?: () => void;
 }
-      {subtext && (
-        <button className="text-h3 text-neutral-04 absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer">
+      {!menuIcon && subtext && (
+        <button
+          type="button"
+          onClick={onSubtextClick}
+          className="text-h3 text-neutral-04 absolute top-1/2 right-4 -translate-y-1/2 cursor-pointer"
+        >
           {subtext}
         </button>
       )}
src/app/friends/page.tsx (2)

14-16: SearchField에 가로 패딩이 필요합니다.

FriendList는 내부에 px-4가 있지만, SearchField는 패딩 없이 렌더링되어 정렬이 맞지 않을 수 있습니다.

♻️ 패딩 추가 제안
       <div className="mt-[30.5px] flex flex-col gap-5">
-        <SearchField />
+        <div className="px-4">
+          <SearchField />
+        </div>
         <FriendList />
       </div>

20-22: FAB 버튼에 접근성 속성과 클릭 핸들러가 필요합니다.

type="button", aria-label, 그리고 onClick 핸들러를 추가해주세요.

♻️ 개선 제안
-          <button className="bg-mint-01 mr-4 ml-auto flex h-[70px] w-[70px] items-center justify-center rounded-full shadow-[0_0_10px_0_rgba(0,0,0,0.10)]">
+          <button
+            type="button"
+            aria-label="친구 추가"
+            onClick={() => {/* TODO: 친구 추가 로직 */}}
+            className="bg-mint-01 mr-4 ml-auto flex h-[70px] w-[70px] cursor-pointer items-center justify-center rounded-full shadow-[0_0_10px_0_rgba(0,0,0,0.10)]"
+          >
             <FriendAddIcon className="h-10 w-10 text-white" />
           </button>
src/components/friends/FrinedList.tsx (1)

5-27: 컴포넌트를 재사용 가능하도록 props를 통해 데이터를 받도록 개선해주세요.

현재 목업 데이터를 직접 import하고 있어 다른 부분에서 재사용하기 어렵습니다. props 패턴으로 변경하면 재사용성을 높일 수 있습니다.

♻️ Props 패턴 제안
 import Image from "next/image";

-import friendListData from "@/mock/friendList.json";
+interface Friend {
+  userId: string;
+  userName: string;
+  profileImg: string;
+}

-export const FriendList = () => {
+interface FriendListProps {
+  friends: Friend[];
+}
+
+export const FriendList = ({ friends }: FriendListProps) => {
   return (
     <section>
       <div className="flex gap-1 px-4">
         <p className="text-sub1-r text-neutral-04">친구</p>
-        <p className="text-sub1-r text-neutral-04">{friendListData.length}</p>
+        <p className="text-sub1-r text-neutral-04">{friends.length}</p>
       </div>
-      {friendListData.map(friend => (
+      {friends.map(friend => (
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d007bd9 and b874903.

⛔ Files ignored due to path filters (3)
  • src/assets/mic-stroke.svg is excluded by !**/*.svg
  • src/assets/person-plus.svg is excluded by !**/*.svg
  • src/assets/search.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • src/app/friends/page.tsx
  • src/components/common/BackHeader.tsx
  • src/components/common/HeaderWithIcon.tsx
  • src/components/friends/FrinedList.tsx
  • src/components/friends/SearchField.tsx
  • src/components/home/Header.tsx
  • src/mock/friendList.json
✅ Files skipped from review due to trivial changes (1)
  • src/mock/friendList.json
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/friends/page.tsx (3)
src/components/common/BackHeader.tsx (1)
  • BackHeader (16-56)
src/components/friends/SearchField.tsx (1)
  • SearchField (4-18)
src/components/friends/FrinedList.tsx (1)
  • FriendList (5-28)
🔇 Additional comments (4)
src/components/common/HeaderWithIcon.tsx (2)

10-10: LGTM!

import 경로가 dailyRecord로 변경되었으며, Next.js 16 및 React 19에 맞는 올바른 import 구조입니다.


12-20: LGTM!

Props 인터페이스가 명확하게 정의되어 있고, havePencil의 기본값이 적절하게 처리되었습니다.

src/components/home/Header.tsx (2)

1-7: LGTM!

import 구문이 적절하게 구성되어 있습니다. Next.js App Router에 맞게 next/navigation에서 useRouter를 올바르게 가져오고 있습니다.


9-11: 알림 상태가 하드코딩되어 있습니다.

현재 isAlarmNew가 항상 true로 초기화되어 컴포넌트가 마운트될 때마다 알림이 새 것으로 표시됩니다. 향후 실제 API 연동 시 서버에서 알림 상태를 받아오도록 구현이 필요합니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +29 to +31
) : (
<AlarmIcon className="text-neutral-03 h-6 w-6 cursor-pointer" />
)}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

isAlarmNew가 false일 때 AlarmIcon의 인터랙션 상태가 일관되지 않습니다.

isAlarmNew가 false일 때 AlarmIcon이 버튼으로 감싸져 있지 않지만 cursor-pointer 클래스가 적용되어 있어 사용자에게 클릭 가능한 것처럼 보입니다. 실제로는 클릭해도 아무 동작이 없어 UX 혼란을 줄 수 있습니다.

🛠️ 제안하는 수정 방안
          ) : (
-            <AlarmIcon className="text-neutral-03 h-6 w-6 cursor-pointer" />
+            <button onClick={() => router.push("/alarms")}>
+              <AlarmIcon className="text-neutral-03 h-6 w-6" />
+            </button>
          )}

또는 알림 페이지 이동이 필요 없다면 cursor-pointer를 제거하세요:

-            <AlarmIcon className="text-neutral-03 h-6 w-6 cursor-pointer" />
+            <AlarmIcon className="text-neutral-03 h-6 w-6" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
) : (
<AlarmIcon className="text-neutral-03 h-6 w-6 cursor-pointer" />
)}
) : (
<AlarmIcon className="text-neutral-03 h-6 w-6" />
)}
🤖 Prompt for AI Agents
In `@src/components/home/Header.tsx` around lines 29 - 31, The AlarmIcon rendering
branch for isAlarmNew = false shows a cursor-pointer but isn’t interactive;
either remove the "cursor-pointer" class from the non-interactive AlarmIcon or
make it interactive to match the true branch by wrapping the AlarmIcon in the
same button element/handler used when isAlarmNew is true (use the same
onClick/navigation logic). Update the JSX around isAlarmNew and the AlarmIcon to
ensure visual affordance matches actual behavior.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/styles/typography.css`:
- Around line 39-43: The CSS custom property name `--text-sub-letter-spacing` is
a typo and does not match the naming pattern used by the other properties
(`--text-sub0`, `--text-sub0--font-weight`, `--text-sub0--line-height`); rename
it to `--text-sub0--letter-spacing` in the `src/styles/typography.css`
definition and update any references/usages that look up
`--text-sub-letter-spacing` so they point to `--text-sub0--letter-spacing`
(search for both `--text-sub-letter-spacing` and `--text-sub0--letter-spacing`
to ensure all occurrences are corrected).

Comment on lines +39 to +43
--text-sub0: 1.375rem;
--text-sub0--font-weight: 400;
--text-sub0--line-height: 147%;
--text-sub-letter-spacing: 0.01375rem;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

--text-sub-letter-spacing 네이밍 오류

Line 42의 --text-sub-letter-spacing는 다른 속성들의 네이밍 패턴(--text-sub0--letter-spacing)과 일치하지 않습니다. 이 오타로 인해 sub0 변형의 letter-spacing을 참조할 때 문제가 발생할 수 있습니다.

🔧 수정 제안
  --text-sub0: 1.375rem;
  --text-sub0--font-weight: 400;
  --text-sub0--line-height: 147%;
- --text-sub-letter-spacing: 0.01375rem;
+ --text-sub0--letter-spacing: 0.01375rem;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--text-sub0: 1.375rem;
--text-sub0--font-weight: 400;
--text-sub0--line-height: 147%;
--text-sub-letter-spacing: 0.01375rem;
--text-sub0: 1.375rem;
--text-sub0--font-weight: 400;
--text-sub0--line-height: 147%;
--text-sub0--letter-spacing: 0.01375rem;
🤖 Prompt for AI Agents
In `@src/styles/typography.css` around lines 39 - 43, The CSS custom property name
`--text-sub-letter-spacing` is a typo and does not match the naming pattern used
by the other properties (`--text-sub0`, `--text-sub0--font-weight`,
`--text-sub0--line-height`); rename it to `--text-sub0--letter-spacing` in the
`src/styles/typography.css` definition and update any references/usages that
look up `--text-sub-letter-spacing` so they point to
`--text-sub0--letter-spacing` (search for both `--text-sub-letter-spacing` and
`--text-sub0--letter-spacing` to ensure all occurrences are corrected).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/components/friends/SearchField.tsx`:
- Around line 25-30: The input in SearchField.tsx is currently uncontrolled
because it doesn't use the searchText prop; make it a controlled component by
wiring the input's value to the searchText prop (e.g., set value={searchText})
and keep the onChange handler calling onChangeSearchText(e.target.value); update
the SearchField component props usage to ensure searchText is passed through and
the input stays synchronized with parent state.
- Around line 15-19: The component currently sets isClick to true only via
onClick and never resets it; update SearchField to use focus events instead:
replace or supplement the onClick handler with onFocus={() => setIsClick(true)}
and onBlur={() => setIsClick(false)} (or attach those handlers to the internal
input element if present) and ensure the wrapper or input is focusable (e.g.,
has a tabIndex or uses the input's focus) so the active styling toggles
correctly; reference state hooks isClick and setIsClick and handlers
onClick/onFocus/onBlur within SearchField.
🧹 Nitpick comments (2)
src/components/friends/ProfileModal.tsx (2)

7-19: 컴포넌트 이름과 실제 구현이 불일치합니다.

ProfileModal이라는 이름은 모달 동작(오버레이, 닫기 버튼, 포커스 관리 등)을 암시하지만, 현재 구현은 단순히 이미지를 래핑한 div입니다.

두 가지 중 하나를 고려해 주세요:

  1. 실제 모달 기능이 필요하다면 모달 관련 기능(backdrop, 닫기 버튼, role="dialog", aria-modal 등)을 추가
  2. 단순 프로필 이미지 표시용이라면 ProfileImage 또는 ProfileAvatar로 이름 변경

10-16: 프로필 이미지 로딩 실패 시 대응이 없습니다.

외부 이미지 URL 로딩 실패 시 사용자 경험이 저하될 수 있습니다. 이미지 로드 실패에 대비한 fallback 이미지나 placeholder 적용을 고려해 주세요.

💡 개선 예시
+import { useState } from "react";
 import Image from "next/image";
+
+const DEFAULT_PROFILE_IMG = "/images/default-profile.png"; // 적절한 경로로 수정

 interface ProfileModalProps {
   profileImg: string;
   userName: string;
 }
 export const ProfileModal = ({ profileImg, userName }: ProfileModalProps) => {
+  const [imgSrc, setImgSrc] = useState(profileImg);
+
   return (
     <div>
       <Image
-        src={profileImg}
+        src={imgSrc}
         alt={`${userName}의 프로필이미지`}
         width={174}
         height={174}
         className="object-cover"
+        onError={() => setImgSrc(DEFAULT_PROFILE_IMG)}
       />
     </div>
   );
 };

Comment on lines 25 to 30
<input
type="text"
placeholder="친구이름을 검색해보세요"
className="text-sub0 text-neutral-01 w-full focus:outline-none"
onChange={e => onChangeSearchText(e.target.value)}
></input>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

searchText prop이 input의 value로 사용되지 않음

searchText prop을 받지만 input의 value 속성으로 설정하지 않아서 비제어 컴포넌트가 됩니다. 부모 컴포넌트의 상태와 input 값이 동기화되지 않으며, 외부에서 searchText를 변경해도 UI에 반영되지 않습니다.

🐛 제어 컴포넌트로 수정
       <input
         type="text"
         placeholder="친구이름을 검색해보세요"
+        value={searchText}
         className="text-sub0 text-neutral-01 w-full focus:outline-none"
         onChange={e => onChangeSearchText(e.target.value)}
-      ></input>
+      />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input
type="text"
placeholder="친구이름을 검색해보세요"
className="text-sub0 text-neutral-01 w-full focus:outline-none"
onChange={e => onChangeSearchText(e.target.value)}
></input>
<input
type="text"
placeholder="친구이름을 검색해보세요"
value={searchText}
className="text-sub0 text-neutral-01 w-full focus:outline-none"
onChange={e => onChangeSearchText(e.target.value)}
/>
🤖 Prompt for AI Agents
In `@src/components/friends/SearchField.tsx` around lines 25 - 30, The input in
SearchField.tsx is currently uncontrolled because it doesn't use the searchText
prop; make it a controlled component by wiring the input's value to the
searchText prop (e.g., set value={searchText}) and keep the onChange handler
calling onChangeSearchText(e.target.value); update the SearchField component
props usage to ensure searchText is passed through and the input stays
synchronized with parent state.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/app/friends/page.tsx`:
- Around line 29-33: BackHeader currently always shows the "편집하기" label because
the isEditMode prop is not passed; update the BackHeader invocation in page.tsx
to pass the current isEditMode state (and optionally the setter if BackHeader
expects it) so the component can switch label/color based on isEditMode — locate
the BackHeader usage and add the prop name isEditMode={isEditMode} (and/or
onToggleEdit={setIsEditMode} if BackHeader needs the setter) so toggling via
setIsEditMode correctly updates the header UI.
🧹 Nitpick comments (1)
src/components/friends/FrinedList.tsx (1)

56-65: 리스트 항목 키보드 접근성 보완 권장

Line 56-65의 클릭 영역이 <div>라 키보드 사용자가 선택/모달 열기를 하기 어렵습니다. role="button", tabIndex, onKeyDown으로 Enter/Space를 처리해 주세요. 키보드/스크린리더 동작 확인도 부탁드립니다.

♿ 제안 변경
         <div
           key={friend.userId}
           className="hover:bg-neutral-10 flex cursor-pointer gap-4 px-4 py-[10px]"
           onClick={() => {
             if (isEditMode) {
               onToggleFriend(friend.userId);
               return;
             }
             profileModalOpen(friend);
           }}
+          role="button"
+          tabIndex={0}
+          onKeyDown={e => {
+            if (e.key !== "Enter" && e.key !== " ") return;
+            e.preventDefault();
+            if (isEditMode) {
+              onToggleFriend(friend.userId);
+              return;
+            }
+            profileModalOpen(friend);
+          }}
         >

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/components/friends/ProfileModal.tsx`:
- Around line 25-33: ProfileModal의 CloseIcon 및 SettingIcon에 onClick만 사용해
키보드/스크린리더 접근성이 없으므로 두 아이콘을 각각 button(type="button") 요소로 감싸고 적절한 aria-label(예:
"닫기", "친구 설정 열기")을 추가하세요; 버튼에 onClick={onClose}와 onClick={() =>
router.push(`/friends/settings/${userId}`)}를 옮기고 필요하면 onKeyDown 핸들러를 제거 또는 유지해
중복 실행을 방지해 접근성을 확보하세요.
- Around line 1-3: 이 파일의 ProfileModal 컴포넌트에서 Next.js 클라이언트 훅인 useRouter()를 사용하지만
파일 최상단에 "use client" 지시문이 빠져 있어 클라이언트 컴포넌트로 인식되지 않습니다; 파일 최상단(임포트 바로 위)에 문자열 지시문
"use client"를 추가하여 ProfileModal 컴포넌트를 클라이언트 컴포넌트로 표시하고 useRouter()와 다른 클라이언트 훅을
정상적으로 사용하도록 수정하세요.

Comment on lines +1 to +3
import Image from "next/image";
import { useRouter } from "next/navigation";

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/components/friends/ProfileModal.tsx | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 192


"use client" 누락으로 인한 클라이언트 훅 사용 불가

useRouter()는 Next.js App Router에서 클라이언트 컴포넌트에서만 사용 가능합니다. 파일 최상단에 "use client" 지시문이 필요합니다.

수정 방법
+ "use client";
 import Image from "next/image";
 import { useRouter } from "next/navigation";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import Image from "next/image";
import { useRouter } from "next/navigation";
"use client";
import Image from "next/image";
import { useRouter } from "next/navigation";
🤖 Prompt for AI Agents
In `@src/components/friends/ProfileModal.tsx` around lines 1 - 3, 이 파일의
ProfileModal 컴포넌트에서 Next.js 클라이언트 훅인 useRouter()를 사용하지만 파일 최상단에 "use client"
지시문이 빠져 있어 클라이언트 컴포넌트로 인식되지 않습니다; 파일 최상단(임포트 바로 위)에 문자열 지시문 "use client"를 추가하여
ProfileModal 컴포넌트를 클라이언트 컴포넌트로 표시하고 useRouter()와 다른 클라이언트 훅을 정상적으로 사용하도록 수정하세요.

Comment on lines +25 to +33
<div className="flex items-center justify-between px-4 py-[10px] pt-[8.5px]">
<CloseIcon
onClick={onClose}
className="text-neutral-09 h-5 w-5 cursor-pointer"
/>
<SettingIcon
className="text-neutral-09 h-6 w-6 cursor-pointer"
onClick={() => router.push(`/friends/settings/${userId}`)}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

아이콘 클릭 요소의 키보드 접근성 확보 필요

SVG에 직접 onClick만 두면 키보드 포커스/스크린리더 접근이 어렵습니다. 버튼으로 감싸고 aria-label을 추가해 주세요.

🔧 제안 수정
-          <CloseIcon
-            onClick={onClose}
-            className="text-neutral-09 h-5 w-5 cursor-pointer"
-          />
-          <SettingIcon
-            className="text-neutral-09 h-6 w-6 cursor-pointer"
-            onClick={() => router.push(`/friends/settings/${userId}`)}
-          />
+          <button
+            type="button"
+            aria-label="닫기"
+            onClick={onClose}
+            className="text-neutral-09 h-5 w-5"
+          >
+            <CloseIcon className="h-5 w-5" />
+          </button>
+          <button
+            type="button"
+            aria-label="설정"
+            onClick={() => router.push(`/friends/settings/${userId}`)}
+            className="text-neutral-09 h-6 w-6"
+          >
+            <SettingIcon className="h-6 w-6" />
+          </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="flex items-center justify-between px-4 py-[10px] pt-[8.5px]">
<CloseIcon
onClick={onClose}
className="text-neutral-09 h-5 w-5 cursor-pointer"
/>
<SettingIcon
className="text-neutral-09 h-6 w-6 cursor-pointer"
onClick={() => router.push(`/friends/settings/${userId}`)}
/>
<div className="flex items-center justify-between px-4 py-[10px] pt-[8.5px]">
<button
type="button"
aria-label="닫기"
onClick={onClose}
className="text-neutral-09 h-5 w-5"
>
<CloseIcon className="h-5 w-5" />
</button>
<button
type="button"
aria-label="설정"
onClick={() => router.push(`/friends/settings/${userId}`)}
className="text-neutral-09 h-6 w-6"
>
<SettingIcon className="h-6 w-6" />
</button>
🤖 Prompt for AI Agents
In `@src/components/friends/ProfileModal.tsx` around lines 25 - 33, ProfileModal의
CloseIcon 및 SettingIcon에 onClick만 사용해 키보드/스크린리더 접근성이 없으므로 두 아이콘을 각각
button(type="button") 요소로 감싸고 적절한 aria-label(예: "닫기", "친구 설정 열기")을 추가하세요; 버튼에
onClick={onClose}와 onClick={() => router.push(`/friends/settings/${userId}`)}를
옮기고 필요하면 onKeyDown 핸들러를 제거 또는 유지해 중복 실행을 방지해 접근성을 확보하세요.

@lemoncurdyogurt lemoncurdyogurt changed the title Feat: 홈화면에서 식물과 대화정도 있는 부분 추가 Feat: 홈화면에서 식물과 대화정도 있는 부분 추가, 친구 프로필 조회, 친구 삭제 Jan 20, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/app/friends/invite/page.tsx`:
- Line 28: The JSX currently contains only the boolean expression {selectTitle
=== "right"} which renders nothing; update the conditional to render the "초대코드"
tab content when selectTitle === "right" by replacing that expression with a
proper conditional render (e.g., logical && or ternary) that returns the desired
markup or a component (create/use an InviteCodeTab or InviteCode component) and
include any necessary props/state (e.g., inviteCode value, copy handler) so
selecting the "right" tab shows the invite code UI instead of just true/false.

In `@src/components/common/GlassStyleHeader.tsx`:
- Line 38: The dynamic Tailwind class `bg-${bgColor}` in GlassStyleHeader.tsx
can be purged at build time; replace it by mapping the bgColor prop to explicit
Tailwind classes (e.g., a small lookup in the GlassStyleHeader component that
returns "bg-neutral-01" etc.) or accept a full validated className prop instead
of a color token, and alternatively add the explicit bg-... variants to the
Tailwind safelist in tailwind.config.js; update the JSX to use the
resolved/static class string rather than `bg-${bgColor}` so Tailwind will
include the styles.
🧹 Nitpick comments (1)
src/components/common/GlassStyleHeader.tsx (1)

4-4: 사용하지 않는 import 제거

useState가 import되었지만 이 컴포넌트에서 사용되지 않습니다.

🧹 제안된 수정
-import { useState } from "react";

</sub>
</section>
)}
{selectTitle === "right"}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

"초대코드" 탭 콘텐츠 누락 - 불완전한 코드

{selectTitle === "right"}는 boolean 표현식으로, React에서 true/false는 렌더링되지 않습니다. "초대코드" 탭 선택 시 표시할 콘텐츠가 없습니다.

🔧 권장 수정 방향
-      {selectTitle === "right"}
+      {selectTitle === "right" && (
+        <section>
+          {/* 초대코드 콘텐츠 추가 */}
+        </section>
+      )}

TODO 항목으로 추적이 필요하시면 이슈 생성을 도와드릴까요?

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{selectTitle === "right"}
{selectTitle === "right" && (
<section>
{/* 초대코드 콘텐츠 추가 */}
</section>
)}
🤖 Prompt for AI Agents
In `@src/app/friends/invite/page.tsx` at line 28, The JSX currently contains only
the boolean expression {selectTitle === "right"} which renders nothing; update
the conditional to render the "초대코드" tab content when selectTitle === "right" by
replacing that expression with a proper conditional render (e.g., logical && or
ternary) that returns the desired markup or a component (create/use an
InviteCodeTab or InviteCode component) and include any necessary props/state
(e.g., inviteCode value, copy handler) so selecting the "right" tab shows the
invite code UI instead of just true/false.

<LeftArrow className="text-neutral-04 h-6 w-6" />
</button>
)}
<div className={`bg-${bgColor} flex gap-[5px] rounded-3xl px-4 py-[6px]`}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

동적 Tailwind 클래스명이 빌드 시 누락될 수 있음

bg-${bgColor} 형태의 동적 클래스 조합은 Tailwind의 purge 메커니즘에서 감지되지 않아 프로덕션 빌드 시 스타일이 적용되지 않을 수 있습니다.

🔧 권장 해결 방안

방안 1: 전체 클래스명을 props로 전달

interface GlassStyleHeaderProps {
  backHeader?: boolean;
  leftText: string;
  rightText: string;
-  bgColor: string;
+  bgColorClass: string; // e.g., "bg-neutral-01"
  selectTitle: "left" | "right";
  onChangeSelectTitle: (value: "left" | "right") => void;
}
-      <div className={`bg-${bgColor} flex gap-[5px] rounded-3xl px-4 py-[6px]`}>
+      <div className={`${bgColorClass} flex gap-[5px] rounded-3xl px-4 py-[6px]`}>

방안 2: safelist에 사용 가능한 색상 추가 (tailwind.config.js)

module.exports = {
  safelist: [
    'bg-neutral-01',
    // 필요한 다른 색상들...
  ],
}
🤖 Prompt for AI Agents
In `@src/components/common/GlassStyleHeader.tsx` at line 38, The dynamic Tailwind
class `bg-${bgColor}` in GlassStyleHeader.tsx can be purged at build time;
replace it by mapping the bgColor prop to explicit Tailwind classes (e.g., a
small lookup in the GlassStyleHeader component that returns "bg-neutral-01"
etc.) or accept a full validated className prop instead of a color token, and
alternatively add the explicit bg-... variants to the Tailwind safelist in
tailwind.config.js; update the JSX to use the resolved/static class string
rather than `bg-${bgColor}` so Tailwind will include the styles.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants